package org.xhy.bytebuddy;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.ModifierReviewable;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 调用拦截注解的方法
        interceptAnnotation();
    }

    /**
     * 拦截实例方法
     * 通过 Byte Buddy 动态生成 Foo 类的子类并拦截实例方法 sayHello
     */
    public static void interceptInstanceMethod() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 使用 Byte Buddy 动态生成 Foo 类的子类
        Class<? extends Foo> instanceMethodDynamicType = new ByteBuddy()
                .subclass(Foo.class) // 生成 Foo 类的子类
                .method(ElementMatchers.named("sayHello")) // 匹配名为 "sayHello" 的实例方法
                .intercept(MethodDelegation.to(InstanceMethodInterceptor.class)) // 将匹配到的方法委托给 InstanceMethodInterceptor
                .make() // 构建动态类
                .load(Foo.class.getClassLoader()) // 加载动态生成的类
                .getLoaded();

        // 通过构造器实例化动态生成的类
        Object instanceMethodInstance = instanceMethodDynamicType.getConstructor().newInstance();
        // 调用拦截后的实例方法，并输出结果
        System.out.println(instanceMethodDynamicType.getMethod("sayHello", String.class).invoke(instanceMethodInstance, "Byte Buddy"));
    }

    /**
     * 拦截被注解的方法
     * 通过 Byte Buddy 拦截带有 TestAnnotation 注解的方法 sayHelloWithAnnotation
     */
    public static void interceptAnnotation() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 使用 Byte Buddy 动态生成 Foo 类的子类
        Class<? extends Foo> instanceMethodDynamicType = new ByteBuddy()
                .subclass(Foo.class) // 生成 Foo 类的子类
                .method(ElementMatchers.isAnnotatedWith(TestAnnotation.class)) // 匹配被 TestAnnotation 注解的方法
                .intercept(MethodDelegation.to(AnnotationMethodInterceptor.class)) // 将匹配到的方法委托给 AnnotationMethodInterceptor
                .make() // 构建动态类
                .load(Foo.class.getClassLoader()) // 加载动态生成的类
                .getLoaded();

        // 通过构造器实例化动态生成的类
        Object instanceMethodInstance = instanceMethodDynamicType.getConstructor().newInstance();
        // 调用拦截后的方法，并输出结果
        System.out.println(instanceMethodDynamicType.getMethod("sayHelloWithAnnotation", String.class).invoke(instanceMethodInstance, "Byte Buddy"));
    }

    /**
     * 拦截构造方法
     * 通过 Byte Buddy 拦截 Foo 类的构造方法，并在调用构造方法时添加拦截逻辑
     */
    public static void interceptConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 使用 Byte Buddy 动态生成 Foo 类的子类，并拦截构造方法
        DynamicType.Unloaded<Foo> subClassUnloaded = new ByteBuddy()
                .subclass(Foo.class) // 生成 Foo 类的子类
                .constructor(ElementMatchers.any()) // 匹配所有的构造方法
                // 因拦截的是对象的构造方法，而构造方法执行逻辑必须涉及到父类的构造方法，因此需要保证父类被正确带哦用
                .intercept(SuperMethodCall.INSTANCE.andThen(
                        MethodDelegation.to(new ConstructorMehtodInterceptor()))) // 在调用原始构造方法之后，执行 ConstructorMehtodInterceptor

                .name("a.b.Sub") // 为生成的类指定新的名称，避免类加载冲突
                .make(); // 构建动态类

        // 加载动态生成的类并实例化
        subClassUnloaded.load(Main.class.getClassLoader())
                .getLoaded()
                .getConstructor()
                .newInstance(); // 调用构造方法进行对象实例化
    }

    /**
     * 拦截静态方法
     * 通过 Byte Buddy 拦截 Foo 类中的静态方法 staticMethod，并在调用时执行增强逻辑
     */
    public static void interceptStaticMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 使用 Byte Buddy 动态生成 Foo 类的子类，并拦截静态方法
        DynamicType.Unloaded<Foo> sayWhatUnload = new ByteBuddy()
                .rebase(Foo.class) // 基于原有 Foo 类字节码生成修改后的类
                .method(ElementMatchers.named("staticMethod").and(ModifierReviewable.OfByteCodeElement::isStatic)) // 匹配名为 "staticMethod" 的静态方法
                .intercept(MethodDelegation.to(new StaticMethodInterceptor())) // 将拦截到的静态方法委托给 StaticMethodInterceptor
                .name("a.b.Sub") // 为生成的类指定新的名称，避免类加载冲突
                .make(); // 构建动态类

        // 加载动态生成的类，并调用拦截后的静态方法
        Class<? extends Foo> loadedClazz = sayWhatUnload.load(Main.class.getClassLoader())
                .getLoaded();

        // 获取并调用静态方法，验证增强逻辑是否生效
        Method sayWhatMethod = loadedClazz.getMethod("staticMethod", String.class);
        sayWhatMethod.invoke(null, "hello world"); // 静态方法不需要实例化，传入 null 作为对象
    }
}
